﻿using System;
using System.IO;

namespace PartCopier
{
    public class PartCopier
    {

        public bool CopyFile(string pathFrom, string fileName, string pathTo) {

            if (GetFileSize(pathFrom, fileName) > GetFreeSpace(pathTo)) throw new Exception("Недостаточно свобоного места на диске");

            if (File.Exists(Path.Combine(pathTo, fileName + ".tmpfile")))
            {
                try
                {
                    PartCopy(Path.Combine(pathFrom, fileName), Path.Combine(pathTo, fileName));
                }
                catch (Exception e) {
                    
                }
            }
            else
            {

                try
                {
                    ASyncCopy(Path.Combine(pathFrom, fileName), Path.Combine(pathTo, fileName));
                }
                catch (Exception e)
                {
                    
                }
            }
            return true;
        }

        public void ASyncCopy(string from, string to)
        {
            if (File.Exists(to))
            {
                try
                {
                    File.Delete(to);
                }
                catch (Exception e)
                {
                    Console.Error.Write(e.Message);
                }
            }
            Copy(from, to);
        }

        private void Copy(string from, string to)
        {
            const int copyBufferSize = 64 * 1024;
            //long copiedSize = 0; long fileSize = GetFileSize(from);
            var tempFile = File.Create(to + ".tmpfile");
            using (var outputFile = File.Create(to))
            {
                using (var inputFile = File.OpenRead(from))
                {
                    var buffer1 = new byte[copyBufferSize];
                    var buffer2 = new byte[copyBufferSize];
                    var inputBuffer = buffer1;
                    int bytesRead;
                    IAsyncResult writeResult = null;
                    while ((bytesRead = inputFile.Read(inputBuffer, 0, copyBufferSize)) != 0)
                    {
                        if (writeResult != null)
                        {
                            writeResult.AsyncWaitHandle.WaitOne();
                            outputFile.EndWrite(writeResult);
                            writeResult = null;
                        }
                        var outputBuffer = inputBuffer;
                        inputBuffer = (inputBuffer == buffer1) ? buffer2 : buffer1;
                        writeResult = outputFile.BeginWrite(outputBuffer, 0, bytesRead, null, null);
                        //copiedSize += bytesRead;
                        //ChangeProgress(copiedSize, fileSize);
                    }
                    if (writeResult != null)
                    {
                        writeResult.AsyncWaitHandle.WaitOne();
                        outputFile.EndWrite(writeResult);
                    }
                }
            }
            tempFile.Close();
            File.Delete(to + ".tmpfile");
        }

        public void PartCopy(string from, string to)
        {
            const int copyBufferSize = 64 * 1024;

            using (var outputFile = File.OpenWrite(to.Substring(0, from.Length - 7)))
            {
                using (var inputFile = File.OpenRead(from.Substring(0, from.Length - 7)))
                {
                    var size = outputFile.Length;
                    var buffer = new byte[copyBufferSize];
                    int bytesRead;
                    inputFile.Position = size;
                    outputFile.Position = size;
                    while ((bytesRead = inputFile.Read(buffer, 0, copyBufferSize)) != 0)
                    {
                        outputFile.Write(buffer, 0, bytesRead);
                        //copiedSize += bytesRead;
                        //ChangeProgress(_copiedSize);
                    }
                }
            }
            File.Delete(to);
        }

        //private void ChangeProgress(long size, long fileSize)
        //{
        //    Console.Write("\r{0}", arg0: ((size / fileSize) * 100) + "%");
        //}

        private long GetFreeSpace(string pathTo)
        {
            var di = new DriveInfo(pathTo.Substring(0, 2));
            return di.TotalFreeSpace;
        }
        private long GetFileSize(string fullPath) {
            var fi = new FileInfo(fullPath);
            return fi.Length;
        }

        private long GetFileSize(string pathFrom, string fileName) {
            var fi = new FileInfo(Path.Combine(pathFrom, fileName));
            return fi.Length;
        }

    }
}
